//
// Created by denglibin on 2021/4/6.
//

#include "camera.h"
#include "cglm.h"
#include "stdbool.h"
static float * getVec3(){
     return (float *)calloc(3, sizeof(float));

}

Camera * initCamera(){
    Camera * camera = (Camera*)calloc(1, sizeof(Camera));
    camera->cameraPos[0] = 0;
    camera->cameraPos[1] = 0;
    camera->cameraPos[2] = 3.0f;

    camera->cameraFront[0] = 0;
    camera->cameraFront[1] = 0;
    camera->cameraFront[2] = -1.0f; //z轴负方向

    camera->cameraUp[0] = 0;
    camera->cameraUp[1] = 1.0f;
    camera->cameraUp[2] = 0;

    camera->cameraSpeed = 0.05f;

    camera->firstMouse = true;

    camera->fovy = 45.0f;

}

void pressKeyMove(Camera *camera, GLFWwindow *window){
    //朝设摄像机方向移动（前进）
    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS){
        vec3 frontScale = GLM_VEC3_ZERO_INIT;
        //数乘法，方向不变，大小改变（由移动速度确定）
        glm_vec3_scale(camera->cameraFront, camera->cameraSpeed, frontScale);
        //移动摄像机后 摄像机新的位置
        vec3 newPos = GLM_VEC3_ZERO_INIT;
        glm_vec3_add(camera->cameraPos, frontScale, newPos);//向量+
        //新的位置拷贝给摄像机位置变量
        glm_vec3_copy(newPos, camera->cameraPos);
        return;
    }
    //朝设摄像机反方向移动（后退）
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS){
        vec3 frontScale = GLM_VEC3_ZERO_INIT;
        //数乘，方向不变，大小改变（由移动速度确定）
        glm_vec3_scale(camera->cameraFront, camera->cameraSpeed, frontScale);
        //移动摄像机后 摄像机新的位置
        vec3 newPos = GLM_VEC3_ZERO_INIT;
        glm_vec3_sub(camera->cameraPos, frontScale, newPos); //向量-
        //新的位置拷贝给摄像机位置变量
        glm_vec3_copy(newPos, camera->cameraPos);
        return;
    }
    //朝设摄像机方向 左移动
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS){
        //得到向左的方向量:上向量叉乘摄像机的方向向量(不能交换顺序)
        vec3  left = GLM_VEC3_ZERO_INIT;
        glm_vec3_cross(camera->cameraUp, camera->cameraFront, left);
        glm_normalize(left); //进行标准化,如果我们没对这个向量进行标准化，最后的叉乘结果会根据cameraFront变量返回大小不同的向量
        //printVec3(left); //-1.000000, 0.000000, 0.000000
        //数乘，方向不变，大小改变（由移动速度确定）
        vec3 leftScale = GLM_VEC3_ZERO_INIT;
        glm_vec3_scale(left, camera->cameraSpeed, leftScale);

        //移动摄像机后 摄像机新的位置
        vec3 newPos = GLM_VEC3_ZERO_INIT;
        glm_vec3_add(camera->cameraPos, leftScale, newPos); //向量+
        //新的位置拷贝给摄像机位置变量
        glm_vec3_copy(newPos, camera->cameraPos);
        return;
    }

    //朝设摄像机方向 右移
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS){
        //得到向右的方向量:摄像机的方向向量 叉乘 上向量(不能交换顺序)
        vec3  right = GLM_VEC3_ZERO_INIT;
        glm_vec3_cross(camera->cameraFront, camera->cameraUp, right);
        glm_normalize(right); //进行标准化,如果我们没对这个向量进行标准化，最后的叉乘结果会根据cameraFront变量返回大小不同的向量
        //printVec3(right); //1.000000, 0.000000, 0.000000
        //数乘，方向不变，大小改变（由移动速度确定）
        vec3 leftScale = GLM_VEC3_ZERO_INIT;
        glm_vec3_scale(right, camera->cameraSpeed, leftScale);

        //移动摄像机后 摄像机新的位置
        vec3 newPos = GLM_VEC3_ZERO_INIT;
        glm_vec3_add(camera->cameraPos, leftScale, newPos); //向量+
        //新的位置拷贝给摄像机位置变量
        glm_vec3_copy(newPos, camera->cameraPos);
        return;
    }
    //朝上移动
    if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS){
        vec3 frontScale = GLM_VEC3_ZERO_INIT;
        //数乘法，方向不变，大小改变（由移动速度确定）
        glm_vec3_scale(camera->cameraUp, camera->cameraSpeed, frontScale);
        //移动摄像机后 摄像机新的位置
        vec3 newPos = GLM_VEC3_ZERO_INIT;
        glm_vec3_add(camera->cameraPos, frontScale, newPos);//向量+
        //新的位置拷贝给摄像机位置变量
        glm_vec3_copy(newPos, camera->cameraPos);
        return;
    }
    //朝下移动
    if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS){
        vec3 frontScale = GLM_VEC3_ZERO_INIT;
        //数乘法，方向不变，大小改变（由移动速度确定）
        glm_vec3_scale(camera->cameraUp, camera->cameraSpeed, frontScale);
        //移动摄像机后 摄像机新的位置
        vec3 newPos = GLM_VEC3_ZERO_INIT;
        glm_vec3_sub(camera->cameraPos, frontScale, newPos);//向量-
        //新的位置拷贝给摄像机位置变量
        glm_vec3_copy(newPos, camera->cameraPos);
        return;
    }


}

/**
 * 移动鼠标改变摄像机方向
 * @param window glfw窗口
 * @param x 光标位置 x
 * @param y 光标位置 y
 */
void mouseDirection(Camera *camera,GLFWwindow *window, double x, double y){
    /**
    * 如果你现在运行代码，你会发现在窗口第一次获取焦点的时候摄像机会突然跳一下。这个问题产生的原因是，在你的鼠标移动进窗口的那一刻，
    * 鼠标回调函数就会被调用，这时候的xpos和ypos会等于鼠标刚刚进入屏幕的那个位置。这通常是一个距离屏幕中心很远的地方，因而产生一个很大的偏移量，
    * 所以就会跳了。我们可以简单的使用一个bool变量检验我们是否是第一次获取鼠标输入，如果是，那么我们先把鼠标的初始位置更新为xpos和ypos值，这样就能解决这个问题
    **/
    if(camera->firstMouse){
        camera->lastX = (float )x;
        camera->lastY = (float )y;
        camera->firstMouse = false;
    }

    float x_offset = (float )x - camera->lastX;
    float y_offset = -((float)y - camera->lastY); //鼠标往上移动，屏幕的y坐标减小，而世界坐标中，网上是y增大，所以取反

    camera->lastX = (float )x;
    camera->lastY = (float )y;

    float sensitivity = 0.05f;//灵敏度
    x_offset *= sensitivity;
    y_offset *= sensitivity;

    camera->yaw += x_offset;
    camera->pitch += y_offset;
    //对于俯仰角，要让用户不能看向高于89度的地方（在90度时视角会发生逆转，所以我们把89度作为极限），同样也不允许小于-89度。这样能够保证用户只能看到天空或脚下，但是不能超越这个限制
    if(camera->pitch > 89.0f)
        camera->pitch =  89.0f;
    if(camera->pitch < -89.0f)
        camera->pitch = -89.0f;
    //注意我们没有给偏航角设置限制，这是因为我们不希望限制用户的水平旋转。当然，给偏航角设置限制也很容易，如果你愿意可以自己实现。
    if(camera->yaw > 89.0f)
        camera->yaw =  89.0f;
    //可以设置一下偏航角的范围
    if(camera->yaw < -89.0f)
        camera->yaw = -89.0f;

    //通过俯仰角和偏航角来计算以得到真正的方向向量

    camera->cameraFront[0] = cosf(camera->pitch/180.0f * CGLM_PI) * sinf(camera->yaw/180.0f * CGLM_PI) ;
    camera->cameraFront[1] = sinf(camera->pitch/180.0f * CGLM_PI);
    camera->cameraFront[2] = cosf(camera->pitch/180.0f * CGLM_PI) * cosf(camera->yaw/180.0f * CGLM_PI) * (-1); //取反表示看向z轴负方向
    glm_normalize(camera->cameraFront); //标准化
}

void scrollFov(Camera *camera,  GLFWwindow *window, double x_offset, double y_offset){
    float sensitivity = 0.05f;//灵敏度
    y_offset *= sensitivity;
    if(camera->fovy >= 1.0f && camera->fovy <= 45.0f)
        camera->fovy -= (float)y_offset;
    if(camera->fovy <= 1.0f)
        camera->fovy = 1.0f;
    if(camera->fovy >= 45.0f)
        camera->fovy = 45.0f;
}
void freeCamera(Camera* camera){
    free(camera);
}

